package com.cloudlead.utils.persistence.codegen.serializer;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.validation.constraints.NotNull;

import org.springframework.beans.factory.annotation.Autowired;

import com.cloudlead.utils.persistence.codegen.PEntityType;
import com.cloudlead.utils.persistence.codegen.type.AutowiredImpl;
import com.cloudlead.utils.persistence.codegen.type.ControllerImpl;
import com.cloudlead.utils.persistence.codegen.type.LabelImpl;
import com.google.common.base.Function;
import com.mysema.codegen.CodeWriter;
import com.mysema.codegen.model.ClassType;
import com.mysema.codegen.model.Parameter;
import com.mysema.codegen.model.Type;
import com.mysema.query.codegen.EntityType;
import com.mysema.query.codegen.Serializer;
import com.mysema.query.codegen.SerializerConfig;
import com.mysema.query.codegen.Supertype;

public class SimpleActionFactorySerializer implements Serializer {
	private static final Function<Type, Parameter> propertyToParameter = new Function<Type, Parameter>() {
		@Override
		public Parameter apply(Type input) {
			return new Parameter(input.getSimpleName(), input);
		}
	};

	private final String javadocSuffix;

	private boolean printSupertype = true;

	private String validatorGroupClassName;

	/**
	 * Create a new BeanSerializer
	 */
	public SimpleActionFactorySerializer() {
		this(" is a Codegen action factory type");
	}

	/**
	 * Create a new BeanSerializer with the given javadoc suffix
	 * 
	 * @param javadocSuffix
	 */
	public SimpleActionFactorySerializer(String javadocSuffix) {
		this.javadocSuffix = javadocSuffix;
	}

	public void serialize(EntityType model, SerializerConfig serializerConfig,
			CodeWriter writer) throws IOException {
		String simpleName = model.getSimpleName();

		// package
		if (!model.getPackageName().isEmpty()) {
			writer.packageDecl(model.getPackageName());
		}

		PEntityType entityType = (com.cloudlead.utils.persistence.codegen.PEntityType) model;
		Supertype parentType = entityType.getParentType();
		ClassType superType = (ClassType) parentType.getType();
		List<Type> parameters = superType.getParameters();
		Type serviceType = parameters.get(0);
		Type voType = parameters.get(1);

		// header
		ControllerImpl repAnnotation = new ControllerImpl(
				Character.toLowerCase(simpleName.charAt(0))
						+ simpleName.substring(1, simpleName.length()));
		model.addAnnotation(repAnnotation);

		// imports
		Set<String> importedClasses = getAnnotationTypes(model);
		if (model.hasLists()) {
			importedClasses.add(List.class.getName());
		}
		if (model.hasCollections()) {
			importedClasses.add(Collection.class.getName());
		}
		if (model.hasSets()) {
			importedClasses.add(Set.class.getName());
		}
		if (model.hasMaps()) {
			importedClasses.add(Map.class.getName());
		}
		importedClasses.add(List.class.getName());
		importedClasses.add(NotNull.class.getName());
		importedClasses.add(Autowired.class.getName());
		importedClasses.add(superType.getFullName());
		importedClasses.add(serviceType.getFullName());
		importedClasses.add(model.getPackageName().substring(0,
				model.getPackageName().length() - 7)
				+ ".BusinessModules");
		importedClasses.add("com.cloudlead.common.worker.annotation.*");
		importedClasses.add("com.cloudlead.common.lang.persistence.*");
		importedClasses.add(validatorGroupClassName + ".*");
		importedClasses.add(voType.getFullName());
		writer.importClasses(importedClasses.toArray(new String[importedClasses
				.size()]));

		// javadoc
		writer.javadoc(simpleName + javadocSuffix);

		for (Annotation annotation : model.getAnnotations()) {
			writer.annotation(annotation);
		}

		Set<Supertype> superTypes = entityType.getSuperTypes();
		if (printSupertype && superTypes != null) {
			List<Type> ifcs = new ArrayList<>();
			for (Supertype sup : superTypes) {
				ifcs.add(sup.getType());
			}
			writer.beginClass(model, parentType.getType(),
					ifcs.toArray(new Type[ifcs.size()]));
		} else {
			writer.beginClass(model);
		}

		Set<Type> types = new HashSet<>();
		types.add(serviceType);
		addFullConstructor(model, writer, types, voType);

		bodyStart(model, voType, writer);

		bodyEnd(model, writer);

		writer.end();
	}

	protected void bodyStart(EntityType model, Type voType, CodeWriter writer)
			throws IOException {
		writer.line("@Action");
		writer.line("@Order(1)");
		writer.line("@ValidatorGroup(Create.class)");
		writer.annotation(new LabelImpl("创建"));
		writer.line("@Override");
		writer.line("public " + voType.getSimpleName() + " create(@NotNull "
				+ voType.getSimpleName()
				+ " vo){");
		writer.line("	return super.create(vo);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(2)");
		writer.line("@ValidatorGroup(Update.class)");
		writer.annotation(new LabelImpl("更新"));
		writer.line("@Override");
		writer.line("public " + voType.getSimpleName() + " update(@NotNull "
				+ voType.getSimpleName()
				+ " vo){");
		writer.line("	return super.update(vo);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(3)");
		writer.annotation(new LabelImpl("批量更新"));
		writer.line("@Override");
		writer.line("public void updateAll(@NotNull " + voType.getSimpleName()
				+ " vo, @NotNull Filter filter){");
		writer.line("	super.updateAll(vo, filter);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(4)");
		writer.annotation(new LabelImpl("根据实例批量更新"));
		writer.line("@Override");
		writer.line("public void updateAllByExample(@NotNull "
				+ voType.getSimpleName() + " vo, @NotNull @Label(\"实例\") "
				+ voType.getSimpleName() + " example){");
		writer.line("	super.updateAllByExample(vo, example);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(5)");
		writer.annotation(new LabelImpl("查看"));
		writer.line("@Override");
		writer.line("public " + voType.getSimpleName()
				+ " view(@NotNull @Label(\"编号\") String id){");
		writer.line("	return super.view(id);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(6)");
		writer.annotation(new LabelImpl("单值查询"));
		writer.line("@Override");
		writer.line("public " + voType.getSimpleName()
				+ " findOne(@NotNull Filter filter){");
		writer.line("	return super.findOne(filter);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(7)");
		writer.annotation(new LabelImpl("根据实例单值查询"));
		writer.line("@Override");
		writer.line("public " + voType.getSimpleName()
				+ " findOneByExample(@NotNull @Label(\"实例\") "
				+ voType.getSimpleName() + " example){");
		writer.line("	return super.findOneByExample(example);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(8)");
		writer.annotation(new LabelImpl("统计"));
		writer.line("@Override");
		writer.line("public long count(Filter filter){");
		writer.line("	return super.count(filter);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(9)");
		writer.annotation(new LabelImpl("根据实例统计"));
		writer.line("@Override");
		writer.line("public long countByExample(@NotNull @Label(\"实例\") "
				+ voType.getSimpleName() + " example){");
		writer.line("	return super.countByExample(example);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(10)");
		writer.annotation(new LabelImpl("列表查询"));
		writer.line("@Override");
		writer.line("public List<" + voType.getSimpleName()
				+ "> list(Filter filter, Sort sort){");
		writer.line("	return super.list(filter, sort);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(11)");
		writer.annotation(new LabelImpl("根据实例列表查询"));
		writer.line("@Override");
		writer.line("public List<" + voType.getSimpleName()
				+ "> listByExample(@NotNull @Label(\"实例\") "
				+ voType.getSimpleName() + " example){");
		writer.line("	return super.listByExample(example);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(12)");
		writer.annotation(new LabelImpl("分页查询"));
		writer.line("@Override");
		writer.line("public Page<" + voType.getSimpleName()
				+ "> findAll(@NotNull PageRequest pageable){");
		writer.line("	return super.findAll(pageable);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(13)");
		writer.annotation(new LabelImpl("删除"));
		writer.line("@Override");
		writer.line("public "
				+ voType.getSimpleName()
				+ " delete(@NotNull @Label(\"编号\") String id){");
		writer.line("	return super.delete(id);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(14)");
		writer.annotation(new LabelImpl("批量删除"));
		writer.line("@Override");
		writer.line("public void deleteAll(Filter filter){");
		writer.line("	super.deleteAll(filter);");
		writer.line("}");
		writer.line("");

		writer.line("@Action");
		writer.line("@Order(15)");
		writer.annotation(new LabelImpl("根据实例批量删除"));
		writer.line("@Override");
		writer.line("public void deleteAllByExample(@Label(\"实例\") "
				+ voType.getSimpleName() + " example){");
		writer.line("	super.deleteAllByExample(example);");
		writer.line("}");
		writer.line("");
	}

	protected void bodyEnd(EntityType model, CodeWriter writer)
			throws IOException {
		// template method
	}

	private Set<String> getAnnotationTypes(EntityType model) {
		Set<String> imports = new HashSet<String>();
		for (Annotation annotation : model.getAnnotations()) {
			imports.add(annotation.annotationType().getName());
		}
		return imports;
	}

	protected void addFullConstructor(EntityType model, CodeWriter writer,
			Set<Type> types, Type voType) throws IOException {

		// full constructor
		Annotation annotation = new AutowiredImpl();
		writer.annotation(annotation);
		writer.beginConstructor(types, propertyToParameter);
		for (Type type : types) {
			writer.line("super(", "BusinessModules.", voType.getSimpleName(),
					", ", type.getSimpleName(), ");");
		}
		writer.end();
	}

	public void setPrintSupertype(boolean printSupertype) {
		this.printSupertype = printSupertype;
	}

	public void setValidatorGroupClassName(String validatorGroupClassName) {
		this.validatorGroupClassName = validatorGroupClassName;
	}

}
